package com.rykj.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

/**
 *
 */
public final class RSAUtils {

    public static final String PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKgyyVfMMyI+eb4/\n" + "qvh13V/mjhbjibxWSC7bsMpsimFQs25xWa/TUg/xxxtOUvE+kpfcGrU4dnqGSb1C" + "gKOBBMZFB9l/knICNvDIaUCGKgXhIEeCoMNrriSLvgqLYNWDSLxlG78CX0U0DPUR" + "tAnYKdyiM0GEYdiU3AWtawLoJvXXAgMBAAECgYEAj0cr8ol1KX+Uz3aG2r30hSPt" + "+Bt+EhxhwuDL+bN6ManAH52ZK8jUx9ZKqL0QtIZ787bAXY0qh/bqy7jhweY+vJDU" + "z+P4TWlKi0LQMPdiiJAQch5XYd0XT8wNG1oYQoV/kpDBawdoes6i1XXyBbJtncSv" + "uNXX60p9DgYGq4r0RNkCQQDZpKbWHMK9IlQuPYLbOW3Jen9Nhe/cdQlhpaybeK0c" + "c6J/8bQSP7nDTdldIx733o/rGwh0CwetBhsasaxIdNcbAkEAxddZAyTVkz8sNSlx" + "y+KRJ/2YaarLd+LcWpfGnb1HmT3O7tXX0GEgextlv/yQ4uk8Xup9BjqgtYoKa3Mj" + "tQPb9QJASC1Ift1O37ZQwHz5toYXGKgdK7JtV2kg0VdGJNEiZ7sZeuDHXP3xNwnM" + "Ogz5qi4PAGMYrJMz2neZ6YJepjDJXQJANOOM+DownlyVBOE/erHm/DnfNOBj5HjR" + "T6ajgifHrD/Jwi+IsjxE4qRAX0K1uPOBIB6vraqeq+lrP65YLVwKwQJBAINArjZB" + "o4OxzIVgV817VqXI+C54RRW7hQI4twDV2uSnSPUj4GM97quBcme1+bZKEIlx3rWG" + "73Y2GreAtQrCPp4=";

    public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];

    private static String RSA = "RSA";

    /**
     * 接口用   获取数据  解密
     */
    public static String deCodeKey(String key) {
        String content = "";
        try {
            PrivateKey privateKey = loadPrivateKey(PRIVATE_KEY);
            byte[] decryptByte = decryptData(Base64.decode(key), privateKey);
            content = new String(decryptByte, "UTF-8");

        } catch (Exception e) {
            e.printStackTrace();
        }
        return content;
    }

    /**
     * 从字符串中加载私钥<br> 加载时使用的是PKCS8EncodedKeySpec（PKCS#8编码的Key指令）。
     */
    public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decode(privateKeyStr);
            // X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("私钥非法");
        } catch (NullPointerException e) {
            throw new Exception("私钥数据为空");
        }
    }

    /**
     * 用私钥解密
     * @param encryptedData
     *         经过encryptedData()加密返回的byte数据
     * @param privateKey
     *         私钥
     */
    public static byte[] decryptData(byte[] encryptedData, PrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            //  Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < encryptedData.length; i += 128) {
                byte[] doFinal = cipher.doFinal(subarray(encryptedData, i, i + 128));
                sb.append(new String(doFinal));
            }
            return sb.toString().getBytes();
        } catch (Exception e) {
            e.printStackTrace();
            return EMPTY_BYTE_ARRAY;
        }
    }

    /**
     * 用公钥加密 <br> 商品分享用 每次加密的字节数，不能超过密钥的长度值减去11
     * @return 加密后的byte型数据
     */  
  /*  public static byte[] encryptData(byte[] data, PublicKey publicKey)
    {  
        try  
        {  
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            // 编码前设定编码方式及密钥  
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
            // 传入编码数据并返回编码结果  
            return cipher.doFinal(data);  
        } catch (Exception e)  
        {  
            e.printStackTrace();  
            return null;  
        }  
    }  */
    public static byte[] subarray(final byte[] array,
            int startIndexInclusive,
            int endIndexExclusive) {
        if (array == null) {
            return null;
        }
        if (startIndexInclusive < 0) {
            startIndexInclusive = 0;
        }
        if (endIndexExclusive > array.length) {
            endIndexExclusive = array.length;
        }
        final int newSize = endIndexExclusive - startIndexInclusive;
        if (newSize <= 0) {
            return EMPTY_BYTE_ARRAY;
        }

        final byte[] subarray = new byte[newSize];
        System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
        return subarray;
    }

    /**
     * 随机生成RSA密钥对(默认密钥长度为1024)
     */
    public static KeyPair generateRSAKeyPair() {
        return generateRSAKeyPair(1024);
    }

    /**
     * 随机生成RSA密钥对
     * @param keyLength
     *         密钥长度，范围：512〜2048<br> 一般1024
     */
    public static KeyPair generateRSAKeyPair(int keyLength) {
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);
            kpg.initialize(keyLength);
            return kpg.genKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 用私钥解密
     * @param encryptedData
     *         经过encryptedData()加密返回的byte数据
     * @param privateKey
     *         私钥
     */
    public static byte[] decryptNoBuffer(byte[] encryptedData, PrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return cipher.doFinal(encryptedData);
        } catch (Exception e) {
            return null;
        }
    }

    public static byte[] encryptData(byte[] data, PublicKey publicKey) {
        try {
            //Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            // Cipher cipher = Cipher.getInstance("RSA");
            // Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding");
            // 编码前设定编码方式及密钥
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);


            byte[] enBytes = null;
            for (int i = 0; i < data.length; i += 64) {
                // 注意要使用2的倍数，否则会出现加密后的内容再解密时为乱码
                byte[] doFinal = cipher.doFinal(subarray(data, i, i + 64));
                enBytes = addAll(enBytes, doFinal);
            }
            // 传入编码数据并返回编码结果
            return enBytes;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static byte[] addAll(final byte[] array1, final byte... array2) {
        if (array1 == null) {
            return clone(array2);
        } else if (array2 == null) {
            return clone(array1);
        }
        final byte[] joinedArray = new byte[array1.length + array2.length];
        System.arraycopy(array1, 0, joinedArray, 0, array1.length);
        System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
        return joinedArray;
    }

    public static byte[] clone(final byte[] array) {
        if (array == null) {
            return null;
        }
        return array.clone();
    }

    /**
     * 通过公钥byte[](publicKey.getEncoded())将公钥还原，适用于RSA算法
     */
    public static PublicKey getPublicKey(byte[] keyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    /**
     * 通过私钥byte[]将公钥还原，适用于RSA算法
     */
    public static PrivateKey getPrivateKey(byte[] keyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    /**
     * 使用N、e值还原公钥
     */
    public static PublicKey getPublicKey(String modulus,
            String publicExponent) throws NoSuchAlgorithmException, InvalidKeySpecException {
        BigInteger bigIntModulus = new BigInteger(modulus);
        BigInteger bigIntPrivateExponent = new BigInteger(publicExponent);
        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    /**
     * 使用N、d值还原私钥
     */
    public static PrivateKey getPrivateKey(String modulus,
            String privateExponent) throws NoSuchAlgorithmException, InvalidKeySpecException {
        BigInteger bigIntModulus = new BigInteger(modulus);
        BigInteger bigIntPrivateExponent = new BigInteger(privateExponent);
        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    /**
     * 从文件中输入流中加载公钥
     * @param in
     *         公钥输入流
     * @throws Exception
     *         加载公钥时产生的异常
     */
    public static PublicKey loadPublicKey(InputStream in) throws Exception {
        try {
            return loadPublicKey(readKey(in));
        } catch (IOException e) {
            throw new Exception("公钥数据流读取错误");
        } catch (NullPointerException e) {
            throw new Exception("公钥输入流为空");
        }
    }

    /**
     * 从字符串中加载公钥
     * @param publicKeyStr
     *         公钥数据字符串
     * @throws Exception
     *         加载公钥时产生的异常
     */
    public static PublicKey loadPublicKey(String publicKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decode(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法");
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空");
        }
    }

    /**
     * 读取密钥信息
     */
    private static String readKey(InputStream in) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(in));
        String readLine = null;
        StringBuilder sb = new StringBuilder();
        while ((readLine = br.readLine()) != null) {
            if (readLine.charAt(0) == '-') {
                continue;
            } else {
                sb.append(readLine);
                sb.append('\r');
            }
        }

        return sb.toString();
    }

    /**
     * 从文件中加载私钥
     * <p/>
     * keyFileName 私钥文件名
     * @return 是否成功
     */
    public static PrivateKey loadPrivateKey(InputStream in) throws Exception {
        try {
            return loadPrivateKey(readKey(in));
        } catch (IOException e) {
            throw new Exception("私钥数据读取错误");
        } catch (NullPointerException e) {
            throw new Exception("私钥输入流为空");
        }
    }

    /**
     * 打印公钥信息
     */
    public static void printPublicKeyInfo(PublicKey publicKey) {
        RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
        System.out.println("----------RSAPublicKey----------");
        System.out.println("Modulus.length=" + rsaPublicKey.getModulus().bitLength());
        System.out.println("Modulus=" + rsaPublicKey.getModulus().toString());
        System.out.println("PublicExponent.length=" + rsaPublicKey.getPublicExponent().bitLength());
        System.out.println("PublicExponent=" + rsaPublicKey.getPublicExponent().toString());
    }

    public static void printPrivateKeyInfo(PrivateKey privateKey) {
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
        System.out.println("----------RSAPrivateKey ----------");
        System.out.println("Modulus.length=" + rsaPrivateKey.getModulus().bitLength());
        System.out.println("Modulus=" + rsaPrivateKey.getModulus().toString());
        System.out.println("PrivateExponent.length=" + rsaPrivateKey.getPrivateExponent()
                .bitLength());
        System.out.println("PrivatecExponent=" + rsaPrivateKey.getPrivateExponent().toString());

    }

}